# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
# This source file is part of the Cangjie project, licensed under Apache-2.0
# with Runtime Library Exception.
#
# See https://cangjie-lang.cn/pages/LICENSE for license information.

from os import path
from random import sample, choice, randint, seed
from itertools import product
import textwrap

integer_types = ['Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64']
number_types = integer_types + ['Float16', 'Float32', 'Float64']
integral_types = number_types + ['String', 'Rune', 'Bool', 'Unit', 'Object', 'Tuple', 'Array', 'Range', 'IntNative', 'UIntNative']
default_value_map = {'Bool' : 'true', 'Unit' : '()', 'Rune' : "'1'", 'Object' : 'Object()', 'String' : '"1"', 'C' : 'C()',
'Tuple' : '(1, 1)', 'Array' : '[1]', 'Range' : '1..2', 'Nothing' : 'nothing()', 'IntNative' : 'iN()', 'UIntNative' : 'uN()'}

def tuple_types(t : str):
  return t[t.index('(')+1:t.index(')')].split(', ')

def default_value(ty : str) -> str:
  if ty in integer_types: return '1' + ty[0].lower() + ty[ty.index('t')+1:]
  if 'Float' in ty: return '1.0f' + ty[5:]
  if ty in classes or ty in impls: return ty+'()'
  if ty in interfaces: return 'C%s.%s()' % (ty, ty.lower())
  if '->' in ty:
    return '{%s => (%s)}' %\
    (', '.join(['x%i : %s' % (i, l[i]) for l in [tuple_types(ty)] for i in range(len(l))]), default_value(ty[ty.index('->')+2:]))
  if '(' in ty: return '(%s)' % ', '.join([default_value(t) for t in tuple_types(ty)])
  return default_value_map[ty]

dir = path.dirname(path.realpath(__file__))
path = dir + '/test_' + path.basename(dir) + '_{}.cj'
template = '''
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

/*
  @Assertion:   4.23(30) Multi-assignment expressions are a special kind of assignment expressions. The left side of
                the equals sign of a multi-assignment expression must be a tuple, the elements inside this tuple must
                be left-valued, the expression on the right side of the equals sign must also be of tuple type, and the
                type of each element of the right tuple must be a subtype of the left-valued type of the corresponding
                position.
  @Description: %s
  @Mode: %s
  @Negative: %s
  @Structure: complex-main
  @Dependencies: aux_a30.cj
  @CompileWarning: %s
  @Comment: Auto-generated by gen.py
*/

main() {
    %s
    return 0
}
'''

seed(123)
limit = 100
classes = { 'A' : 'JI', 'B' : 'AJI', 'C' : 'BAJI', 'D' : 'AJI'}
interfaces = { 'I' : '', 'J' : 'I', 'K' : 'JI', 'L' : 'I'}
impls = { 'CI' : 'I', 'CJ' : 'JI', 'CK' : 'KJI', 'CL' : 'LI'}
custom_type_dict_list = [classes, interfaces, impls]
custom_type_dict = {**classes, **interfaces, **impls}
custom_types = [t for c in custom_type_dict_list for t in c]
types = integral_types + custom_types

def subtype(t0, t1):
  if isinstance(t0, tuple): return all([subtype(t0[i], t1[i]) for i in range(len(t0))])
  return t0 == t1 or any(t0 in c and t1 in c[t0] for c in [classes, interfaces, impls])

def prod_list(s1, s2):
  return list(product(s1, s2))

def warning(ty : str):
  return 'ignore' if ty in ['Unit', 'Nothing'] else 'no'

def issue(ty : str):
  return '\n  @Issue: 6518' if ty == 'Nothing' else ''

def write_counted(contents : str):
  global counter
  with open(path.format(str(counter).zfill(3)), 'w') as file:
    file.write(contents)
    counter += 1

def keyword(ty : str):
  if ty in interfaces: return 'interface'
  if ty in custom_types: return 'open class'
  return 'extend'

def name(ty : str, c : list):
  if ty in integral_types: return ty
  return ty + ((' <: ' + custom_type_dict[ty][0]) if (custom_type_dict[ty] and custom_type_dict[ty][0] in c) else '')

def mangle(ty : str):
  return ty.replace(' ', '').replace('(', 'b').replace(')', 'e').replace(',', 'c').replace('->', 'to')

def format(expr: str) -> str:
  return '\n'.join(textwrap.wrap(expr, 119, subsequent_indent=' '*8))

subtypes = [(t, t) for t in integral_types] + [('Nothing', t) for t in types] + [('A', 'Object')]
non_subtypes = sample([(t0, t1) for t0, t1 in product(integral_types, integral_types) if t0 != t1], limit)
subtypes += [('(A, B, C)', '(%s, %s, %s)' % (t0, t1, t2))
             for t0, t1, t2 in product(classes, repeat=3) if subtype(('A', 'B', 'C'), (t0, t1, t2))]
non_subtypes += [('(A, B, C)', '(%s, %s, %s)' % (t0, t1, t2))
             for t0, t1, t2 in product(classes, repeat=3) if not subtype(('A', 'B', 'C'), (t0, t1, t2))]
subtypes += [('(A, B) -> (A, C)', '(%s, %s) -> (%s, %s)' % (t0, t1, t2, t3))
    for t0, t1, t2, t3 in product(classes, repeat=4) if subtype((t0, t1), ('A', 'B')) and subtype(('A', 'C'), (t2, t3))]
non_subtypes += [('(A, B) -> (A, C)', '(%s, %s) -> (%s, %s)' % (t0, t1, t2, t3))
    for t0, t1, t2, t3 in product(classes, repeat=4) if not (subtype((t0, t1), ('A', 'B')) and subtype(('A', 'C'), (t2, t3)))]

for c in [prod_list(interfaces, interfaces), prod_list(classes, classes), prod_list(impls, interfaces), prod_list(classes, interfaces)]:
    subtypes += [(t0, t1) for t0, t1 in c if subtype(t0, t1)]
    non_subtypes += [(t0, t1) for t0, t1 in c if not subtype(t0, t1)]

counter = 1
positive_test = template % ('''Checks that multi-assignment is permitted when for every type of an element in the left tuple the
                type of the corresponding element in the right tuple is a subtype is this type.''', 'run', 'no', 'ignore', 
    '\n    '.join(['var v%s : %s' % (mangle(t), t) for t in set([t for _, t in subtypes]) ]) + '\n\n' +
    '\n'.join([format('    (%s) = (%s)' % (', '.join(['v' + mangle(t) for _, t in tys]), (', '.join([default_value(t) for t, _ in tys]))))
        for _ in range(300) for tys in [[choice(subtypes) for _ in range(randint(2, 8))]]]))
negative_tests = [template % ('''Checks that multi-assignment is not permitted when some element of type %s
                in the right tuple is matched with an element of type %s in the left tuple.''' % (t1, t0) , 'compileonly', 'yes', 'no',
'''var v : %s
    (v, _) = (%s, 1)''' % (t1, default_value(t0))) for t0, t1 in non_subtypes]
write_counted(positive_test)
counter += 20
for test in negative_tests:
  write_counted(test)
